package scu.maqiang.mesh;

import scu.maqiang.numeric.CG;
import scu.maqiang.numeric.MVO;

import java.io.*;
import java.util.Arrays;
import java.util.Scanner;

public class Mesh3T3S extends Mesh {
	public Mesh3T3S() {
		super();
		nDim = 3;
		nPerElement = 3;
		nPerBoundary = 0;
		nEdgePerElement = 3;
		nEdgePerBoundary = 2;
		nBoundaryPerElement = 3;
		tecplotType = "FETRIANGLE";
		meditMeshType = new String[]{"Vertices", "Edges", "Points"};
	}

	public Mesh3T3S reflectXY(Mesh3T3S mesh0) {
		nv = mesh0.nv;
		nt = mesh0.nt;
		nodes = new double[nv][];
		nodeLabel = mesh0.nodeLabel.clone();
		elements = new int[nt][];
		elementLabel = mesh0.elementLabel.clone();
		for (int i = 0; i < nv; i++) {
			double[] node = mesh0.nodes[i];
			nodes[i] = new double[] { node[0], node[1], -node[2] };
		}

		for (int i = 0; i < mesh0.nt; i++) {
			int[] ele = mesh0.getElement(i);
			elements[i] = new int[] { ele[0], ele[2], ele[1] };
		}
		return this;
	}

	public Mesh3T3S constructMesh(double[][] nodes, int[][] eles) {
		nv = nodes.length;
		nt = eles.length;
		nodes = new double[nv][];
		nodeLabel = new int[nv];
		elements = new int[nt][];
		elementLabel = new int[nt];
		for(int i = 0; i < nv; i++) {
			nodes[i] = nodes[i].clone();
		}
		for(int i = 0; i < nt; i++) {
			elements[i] = eles[i].clone();
		}
		extractBoundarysFromElements();
		return this;
	}

	public Mesh3T3S(Mesh2T3 mesh) {
		this();
		int nv = mesh.nv;
		int nt = mesh.nt;
		int nb = mesh.nb;
		this.initMesh0(nv, nt, nb);
		for(int i = 0; i < nv; i++) {
			this.nodes[i] = mesh.nodes[i].clone();
			this.nodeLabel[i] = mesh.nodeLabel[i];
		}
		for(int i = 0; i < nt; i++) {
			this.elements[i] = mesh.elements[i].clone();
			this.elementLabel[i] = mesh.elementLabel[i];
		}
		for(int i = 0; i < nt; i++) {
			this.boundaries[i] = mesh.boundaries[i].clone();
			this.boundaryLabel[i] = mesh.boundaryLabel[i];
		}
	}

	public void saveMesh_mesh(String fileName) {
		try (BufferedWriter bw = new BufferedWriter(new FileWriter(fileName))) {
			bw.write("MeshVersionFormatted 1\n\n");
			bw.write("Dimension 3\n\n");
			bw.write("Vertices\n");
			bw.write(nv +"\n");
			for (int i = 0; i < nv; i++) {
				double[] node = nodes[i];
				for (int j = 0, n = node.length; j < n; j++) {
					bw.write(node[j] + "\t ");
				}
				bw.write(nodeLabel[i] + "\n");
			}
			bw.write("\nTriangles\n");
			bw.write(nt + "\n");
			for (int i = 0; i < nt; i++) {
				int[] ele = elements[i];
				for (int j = 0, n = ele.length; j < n; j++) {
					bw.write((ele[j] + 1) + "\t ");
				}
				bw.write(elementLabel[i] + "\n");
			}
			bw.write("\nEdges\n");
			bw.write(nb + "\n");
			for (int i = 0; i < nb; i++) {
				int[] ele = boundaries[i];
				for (int j = 0, n = ele.length; j < n; j++) {
					bw.write((ele[j] + 1) + "\t ");
				}
				bw.write(boundaryLabel[i] + "\n");
			}
			bw.write("\nEnd\n");
			bw.write("\nEnd\n");
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	@Override
	public double[] getinteriorPointInElement(double[][] coord) {
		double[] x = new double[3];
		double[] y = new double[3];
		double[] z = new double[3];
		for(int i = 0; i < 3; i++) {
			x[i] = coord[i][0];
			y[i] = coord[i][1];
			z[i] = coord[i][2];
		}
		return new double[]{ MVO.average(x), MVO.average(y), MVO.average(z)};
	}

	@Override
	public double[] getinteriorPointOnBoundary(double[][] coord) {
		double[] x = {coord[0][0], coord[1][0]};
		double[] y = {coord[0][1], coord[1][1]};
		double[] z = {coord[0][2], coord[1][2]};
		return new double[]{ MVO.average(x), MVO.average(y), MVO.average(z)};
	}

	@Override
	public int[][] getBoundarysFromElement(int i) {
		int[][] bd = new int[nBoundaryPerElement][];
		int[] ele = elements[i];
		bd[0] = new int[] {ele[0], ele[1]};
		bd[1] = new int[] {ele[1], ele[2]};
		bd[2] = new int[] {ele[2], ele[0]};
		return bd;
	}

	@Override
	public boolean checkMesh(double[][] coord) {
		// TODO Auto-generated method stub
		return false;
	}
	
	@Override
	public int getTecplotNt() {
		return nt;
	}

	@Override
	public int[][] getEdgesFromElement(int i) {
		int[][] result = new int[3][];
		int[] ele = elements[i];
		result[0] = new int[] {ele[0], ele[1]};
		result[1] = new int[] {ele[1], ele[2]};
		result[2] = new int[] {ele[2], ele[0]};
		return result;
	}

	public void shrink(double[] xyz, double length) {
		double[][] distance = new double[nv][nDim];
		double[][] coord = new double[nPerElement][nDim];
		double[][] tet = new double[4][];
		int[] count = new int[nv];
		for(int i = 0; i < nt; i++) {
			int[] ele = getElement(i);
			getCoordPerElement(ele, coord);
			tet[0] = coord[0];
			tet[1] = coord[1];
			tet[2] = coord[2];
			tet[3] = xyz;
			double volume = CG.volume3DT4_Signed(tet);
			double[] normal = CG.unitOutNormal3DT3(coord);
			double sign = Math.signum(volume);
			System.out.println(sign);
			distance[ele[0]][0] += sign * normal[0] * length;
			distance[ele[1]][0] += sign * normal[0] * length;
			distance[ele[2]][0] += sign * normal[0] * length;
			distance[ele[0]][1] += sign * normal[1] * length;
			distance[ele[1]][1] += sign * normal[1] * length;
			distance[ele[2]][1] += sign * normal[1] * length;
			distance[ele[0]][2] += sign * normal[2] * length;
			distance[ele[1]][2] += sign * normal[2] * length;
			distance[ele[2]][2] += sign * normal[2] * length;
			count[ele[0]]++;
			count[ele[1]]++;
			count[ele[2]]++;
		}

		for(int i = 0; i < nv; i++) {
			nodes[i][0] += distance[i][0] / count[i];
			nodes[i][1] += distance[i][1] / count[i];
			nodes[i][2] += distance[i][2] / count[i];
		}
	}
}
